package it.unica.informatica.enotes;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.mobeta.android.dslv.DragSortListView;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;

import it.unica.informatica.enotes.database.DatabaseAdapter;
import it.unica.informatica.enotes.manager.NotesManager;
import it.unica.informatica.enotes.notes.AttachmentItem;
import it.unica.informatica.enotes.manager.MultimediaManager;
import it.unica.informatica.enotes.adapter.MultimediaItemAdapter;
import it.unica.informatica.enotes.notes.Note;
import it.unica.informatica.enotes.adapter.TagItemAdapter;

/**
 * This activity provides the necessary operations to show, modify, delete and send a note
 * @author Mirko Marras
 * @version 1.0
 */
public class NotesActivity extends ActionBarActivity {

    /**********************************************************************************************
     *                                       STATE VARIABLES                                      *
     **********************************************************************************************/

    private DatabaseAdapter databaseAdapter;

    private static final String KEY_OLD_NOTE = "old_note";
    private static final String KEY_CURRENT_NOTE = "current_note";
    private Note oldNote;
    private Note currentNote;

    private static final String KEY_NOTE_ID = "note_id";
    private Long currentNoteId;

    private static final String KEY_CURRENT_TAB = "current_tab";
    private static final int[] TAB_IDS =  { R.id.action_view_text, R.id.action_view_attachments, R.id.action_view_tags };
    private int currentTab;

    private static final String KEY_EDIT_STATE = "edit_state";
    private boolean editState;

    /**********************************************************************************************
     *                     CONSTANTS FOR INTENT TO REQUEST MULTIMEDIA CONTENT                     *
     **********************************************************************************************/

    private static final int REQUEST_VIEW = 0;

    public static final String TEMP_IMAGE_FILE = "ImageFile.jpg";
    private static final int REQUEST_CAMERA = 1;
    private static final int SELECT_IMAGE = 2;

    public static final String TEMP_VIDEO_FILE = "VideoFile.mp4";
    private static final int REQUEST_VIDEO = 3;
    private static final int SELECT_VIDEO = 4;

    private static final int REQUEST_AUDIO = 5;
    private static final int SELECT_AUDIO = 6;

    /**********************************************************************************************
     *                                 VARIABLES FOR ACTIVITY VIEWS                               *
     **********************************************************************************************/

    private Menu actionBarMenu;

    /* VARIABLES FOR ACTIVITY TABS */
    private Button tabButtonText;
    private Button tabButtonAttachments;
    private Button tabButtonTags;

    /* VARIABLES FOR TEXT CONTENT TAB */
    private ScrollView mainScrollView;
    private EditText titleText;
    private EditText contentText;
    private TextView lastModifiedDate;

    /* VARIABLES FOR ATTACHMENTS TAB */
    private LinearLayout mainAttachmentView;
    private LinearLayout bottomButtons;
    private ArrayList<AttachmentItem> attachments;
    private DragSortListView multimediaDragSortListView ;
    private MultimediaItemAdapter multimediaAdapter;
    private TextView emptyAttachmentView;

    /* VARIABLES FOR TAGS TAB */
    private LinearLayout mainTagView;
    private LinearLayout tagLayout;
    private EditText insertTag;
    private ArrayList<String> tags;
    private DragSortListView  tagsDragSortListView ;
    private TagItemAdapter tagAdapter;
    private TextView emptyTagView;

    /**********************************************************************************************
     *                         ACTIVITY LIFECYCLE METHODS AND SIMILAR                             *
     **********************************************************************************************/

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notes);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        /* Create the database used to perform queries */
        databaseAdapter = new DatabaseAdapter(getApplicationContext());
        databaseAdapter.open();

        /* Set the base directory where the app stores temporary files */
        MultimediaManager.setBaseDirectory(this);

        /* Retrieve state variables for the activity and the note */
        initializeStateVariables(savedInstanceState);

        /* Perform bind from view layout objects to activity variables */
        initializeViews();

        /* Set the content view layout object to the values of the note with the ID retrieved */
        populateFields();

        /* If the activity is started by another application that requests a note import, retrieve and save it*/
        if(getIntent().getData() != null) {
            new ImportTask(this).execute();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        databaseAdapter.close();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        /* Save the ID of the current note to retrieve it when the activity is recreated */
        outState.putSerializable(KEY_NOTE_ID, currentNoteId);

        /* Save the EditState of the current note to retrieve it when the activity is recreated */
        outState.putSerializable(KEY_EDIT_STATE, editState);

        /* Save the curretn tab to retrieve it when the activity is recreated */
        outState.putSerializable(KEY_CURRENT_TAB, currentTab);

        /* Save the curretn note to retrieve it when the activity is recreated */
        outState.putParcelable(KEY_CURRENT_NOTE, currentNote);

        /* Save the curretn note to retrieve it when the activity is recreated */
        outState.putParcelable(KEY_OLD_NOTE, oldNote);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        actionBarMenu = menu;

        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_notes, menu);

        /* Disable views based on the state of the activity: EDIT/ONLY VIEW note contents */
        onActivityStateChange();

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                if(editState && !currentNote.isEmpty() && !currentNote.equals(oldNote)) {
                    new SaveTask(this, true).execute();
                }
                else {
                    onBackPressed();
                }
                return true;
            case R.id.action_edit:
                /* Activate views to modify note contents */
                editState = true;
                onActivityStateChange();
                return true;
            case R.id.action_save:
                /* Save note content to the database */
                if (!currentNote.isEmpty()) {
                    if(!currentNote.equals(oldNote)) {
                        new SaveTask(this, false).execute();
                    }
                }
                else {
                    Toast.makeText(getBaseContext(), R.string.toast_empty_note, Toast.LENGTH_SHORT).show();
                }
                return true;
            case R.id.action_discard:
                /* Delete the note from the database */
                deleteNote();
                return true;
            case R.id.action_new_email:
                /* Create a file that contains the content of the note and send it by mail */
                sendNoteByEmail();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**********************************************************************************************
     *                           METHODS OF THE ACTION BAR BUTTONS                                *
     **********************************************************************************************/

    /**
     * Return to the MainScreenActivity when the user press the back button from this activity
     */
    @Override
    public void onBackPressed() {
        if(editState && !currentNote.equals(oldNote)) {
            new SaveTask(this, true).execute();
        }
        else {
            super.onBackPressed();
        }
    }

    /**
     * Delete the current note from the database if exists or discard it if it is a draft
     */
    private void deleteNote() {
        /* Verify if the note contains some data and delete it if it is already saved into the database  */
         if (!currentNote.isEmpty()) {

            // Create and show a dialog to ask a confirm from the user
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle(R.string.action_delete_dialog_title);
            builder.setMessage(R.string.action_delete_dialog_message);

            builder.setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    // If the note already exists, it is deleted from the database
                    if (currentNoteId != null) {
                        NotesManager.deleteNoteById(databaseAdapter, currentNote.getId());
                        Toast.makeText(getBaseContext(), R.string.toast_delete_note, Toast.LENGTH_SHORT).show();
                    } else { // If the note doesn't exists and it is only a draft
                        Toast.makeText(getBaseContext(), R.string.toast_delete_draft, Toast.LENGTH_SHORT).show();
                    }

                    finish();
                }
            });

            builder.setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                }
            });

            builder.show();

        }
        else {
            finish();
        }

    }

    /**
     * Create and start an intent to send an email with a note contents file as attachment
     */
    private void sendNoteByEmail() {
        /* Verify if the note contains some data and create an email intent to send it */
        if (!currentNote.isEmpty()) {
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setType("message/rfc822");
            intent.putExtra(Intent.EXTRA_SUBJECT, currentNote.getTitle());
            intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.action_mail_dialog_message));
            intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(MultimediaManager.createFileForMail(currentNote)));
            startActivity(Intent.createChooser(intent, getString(R.string.action_mail_dialog_title)));
        } else { /* If the note doesn't contain any data */
            Toast.makeText(getBaseContext(), R.string.toast_empty_note, Toast.LENGTH_SHORT).show();
        }
    }

    /**********************************************************************************************
     *        METHODS ASSOCIATED WITH AN ON CLICK LISTENER FOR BUTTONS IN THE LAYOUT              *
     **********************************************************************************************/

    /**
     * Show or hide view objects of the layout based on the tab button selected by the user
     * @param v The tab button selected by the user
     */
    public void onTabButtonSelected(View v) {
        /* Perform a selection based on the clicked tab: TEXT CONTENT, ATTACHMENTS and TAGS tabs */
        switch (v.getId()) {

            /*************************** CURRENT TAB: TEXT CONTENT ********************************/
            case R.id.action_view_text:
                currentTab = TAB_IDS[0];
                /* Color to blue the TEXT CONTENT tab button and color to grey other tab buttons */
                tabButtonText.setSelected(true);
                tabButtonText.setTextColor(Color.argb(0xff, 0x3b, 0x59, 0x98));
                tabButtonAttachments.setSelected(false);
                tabButtonAttachments.setTextColor(Color.argb(0xff, 0x80, 0x80, 0x80));
                tabButtonTags.setSelected(false);
                tabButtonTags.setTextColor(Color.argb(0xff, 0x80, 0x80, 0x80));
                /* Set to VISIBLE the EditTexts for the title and the text content of the note */
                mainScrollView.setVisibility(View.VISIBLE);
                /* Set to GONE the views for the ATTACHMENTS tab and the TAGS tab */
                mainAttachmentView.setVisibility(View.GONE);
                bottomButtons.setVisibility(View.GONE);
                tagLayout.setVisibility(View.GONE);
                mainTagView.setVisibility(View.GONE);
                break;

            /*************************** CURRENT TAB: ATTACHMENTS *********************************/
            case R.id.action_view_attachments:
                currentTab = TAB_IDS[1];
                /* Color to blue the ATTACHMENTS tab button and color to grey other tab buttons */
                tabButtonText.setSelected(false);
                tabButtonText.setTextColor(Color.argb(0xff, 0x80, 0x80, 0x80));
                tabButtonAttachments.setSelected(true);
                tabButtonAttachments.setTextColor(Color.argb(0xff, 0x3b, 0x59, 0x98));
                tabButtonTags.setSelected(false);
                tabButtonTags.setTextColor(Color.argb(0xff, 0x80, 0x80, 0x80));
                /* Set to GONE the views for the TEXT CONTENT tab */
                mainScrollView.setVisibility(View.GONE);
                /* Set to VISIBLE the view for the ATTACHMENTS tab */
                mainAttachmentView.setVisibility(View.VISIBLE);
                bottomButtons.setVisibility((editState) ? View.VISIBLE : View.GONE);
                /* Set to GONE the views for the TAGS tab */
                tagLayout.setVisibility(View.GONE);
                mainTagView.setVisibility(View.GONE);
                break;

            /********************************* CURRENT TAB: TAGS **********************************/
            case R.id.action_view_tags:
                currentTab = TAB_IDS[2];
                /* Color to blue the TAGS tab button and color to grey other tab buttons */
                tabButtonText.setSelected(false);
                tabButtonText.setTextColor(Color.argb(0xff, 0x80, 0x80, 0x80));
                tabButtonAttachments.setSelected(false);
                tabButtonAttachments.setTextColor(Color.argb(0xff, 0x80, 0x80, 0x80));
                tabButtonTags.setSelected(true);
                tabButtonTags.setTextColor(Color.argb(0xff, 0x3b, 0x59, 0x98));
                /* Set to GONE the views for the TEXT CONTENT tab and the ATTACHMENTS tab */
                mainScrollView.setVisibility(View.GONE);
                mainAttachmentView.setVisibility(View.GONE);
                bottomButtons.setVisibility(View.GONE);
                /* Set to VISIBLE the view for the TAGS tab */
                tagLayout.setVisibility((editState) ? View.VISIBLE : View.GONE);
                mainTagView.setVisibility(View.VISIBLE);
                break;
            default:
        }
    }

    /**
     * Show a specific intent to take a multimedia content based on the button selected by the user
     * @param v The button associated to a type of multimedia content (Image, Audio, Video)
     */
    public void onInsertAttachmentSelected(View v) {
        switch (v.getId()) {
            case R.id.action_camera:
                /* Show a dialog to choose how to take an IMAGE from the device */
                onSelectImageRequest();
                break;
            case R.id.action_mic:
                /* Show a dialog to choose how to take an AUDIO TRACK from the device */
                onSelectAudioRequest();
                break;
            case R.id.action_video:
                /* Show a dialog to choose how to take a VIDEO from the device */
                onSelectVideoRequest();
                break;
            default:
        }
    }

    /**
     * Add the tag inserted by the user to the tags list for the current note
     * @param v The button clicked by the user to add a new tag
     */
    public void onInsertTagSelected(View v) {
        /* Read the tag inserted by the user and transform it to lower case */
        String readTag = insertTag.getText().toString().toLowerCase();

        /* If the tag is a single word with at least one letter, add the tag to the tags list */
        if(!tags.contains(readTag)) {
            if (readTag.length() > 0 && !readTag.contains(" ")) {
                tags.add(0, readTag);
                tagAdapter.notifyDataSetChanged();
                insertTag.setText("");
                tabButtonTags.setText(tabButtonTags.getTag() + " (" + tags.size() + ")");
                Toast.makeText(getBaseContext(), R.string.toast_tag_inserted, Toast.LENGTH_SHORT).show();
            } else { /* If the tag string is empty or contains more than one word */
                Toast.makeText(getBaseContext(), R.string.toast_tag_error, Toast.LENGTH_SHORT).show();
            }
        }
        else { /* If the tag string already exists in the list */
            Toast.makeText(getBaseContext(), R.string.toast_tag_duplicate, Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Sort the tags list in lessico-order, unless when the user can perform custom sort
     * @param v The button clicked by the user to sort the tags list
     */
    public void onSortTagsPressed(View v) {
        // Create and show a dialog to ask a confirm from the user
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.action_sort_dialog_title);
        builder.setMessage(R.string.action_sort_dialog_message);

        builder.setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                Collections.sort(tags);
                tagAdapter.notifyDataSetChanged();
                Toast.makeText(getBaseContext(), R.string.action_sort_dialog_correct, Toast.LENGTH_SHORT).show();
            }
        });

        builder.setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
            }
        });

        builder.show();
    }

    /**
     * Show a dialog to allow the user to select how to take a new image as attachment for the current note
     */
    private void onSelectImageRequest() {
        /* Set the title of the available choices to take an image from the device */
        final String[] items = {    getString(R.string.action_camera_dialog_camera),
                                    getString(R.string.action_camera_dialog_library) };

        /* Create and show a dialog with which the user selects the method to take the image */
        AlertDialog.Builder builder = new AlertDialog.Builder(NotesActivity.this);
        builder.setTitle(getString(R.string.action_camera_dialog_title) + " (" + MultimediaManager.ALLOWED_MAX_IMAGE_MB + ")");

        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int item) {
                /* If the user selects to take an image by the device camera */
                if (items[item].equals(getString(R.string.action_camera_dialog_camera))) {
                    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    File f = new File(android.os.Environment.getExternalStorageDirectory(), TEMP_IMAGE_FILE);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
                    if (intent.resolveActivity(getPackageManager()) != null) {
                        startActivityForResult(intent, REQUEST_CAMERA);
                    } else {
                        Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                    }
                // If the user selects to pick an image from another application of the device
                } else if (items[item].equals(getString(R.string.action_camera_dialog_library))) {
                    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                    intent.setType("image/*");
                    if (intent.resolveActivity(getPackageManager()) != null) {
                        startActivityForResult(Intent.createChooser(intent, getString(R.string.action_camera_dialog_title)), SELECT_IMAGE);
                    } else {
                        Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

        builder.show();
    }

    /**
     * Show a dialog to allow the user to select how to take a new video as attachment for the current note
     */
    private void onSelectVideoRequest() {
        /* Set the title of the available choices to take a video from the device */
        final String[] items = {    getString(R.string.action_video_dialog_camera),
                                    getString(R.string.action_video_dialog_library) };

        /* Create and show a dialog with which the user selects the method to take the video */
        AlertDialog.Builder builder = new AlertDialog.Builder(NotesActivity.this);
        builder.setTitle(getString(R.string.action_video_dialog_title) + " (" + MultimediaManager.ALLOWED_MAX_VIDEO_MB + ")");

        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int item) {
                /* If the user selects to take a video by the device camera */
                if (items[item].equals(getString(R.string.action_video_dialog_camera))) {
                    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                    File f = new File(android.os.Environment.getExternalStorageDirectory(), TEMP_VIDEO_FILE);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
                    if (intent.resolveActivity(getPackageManager()) != null) {
                        startActivityForResult(intent, REQUEST_VIDEO);
                    } else {
                        Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                    }
                // If the user selects to pick a video from another application of the device
                } else if (items[item].equals(getString(R.string.action_video_dialog_library))) {
                    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                    intent.setType("video/*");
                    if (intent.resolveActivity(getPackageManager()) != null) {
                        startActivityForResult(Intent.createChooser(intent, getString(R.string.action_video_dialog_title)), SELECT_VIDEO);
                    } else {
                        Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

        builder.show();
    }

    /**
     * Show a dialog to allow the user to select how to take a new audio track as attachment for the current note
     */
    private void onSelectAudioRequest() {
        /* Set the title of the available choices to take a video from the device */
        final String[] items = {    getString(R.string.action_audio_dialog_audio),
                                    getString(R.string.action_audio_dialog_library) };

        /* Create and show a dialog with which the user selects the method to take the audio track */
        AlertDialog.Builder builder = new AlertDialog.Builder(NotesActivity.this);
        builder.setTitle(getString(R.string.action_audio_dialog_title) + " (" + MultimediaManager.ALLOWED_MAX_AUDIO_MB + ")");

        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int item) {
                /* If the user selects to take an audio track by the device recorder */
                if (items[item].equals(getString(R.string.action_audio_dialog_audio))) {
                    Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
                    if (intent.resolveActivity(getPackageManager()) != null) {
                        startActivityForResult(intent, REQUEST_AUDIO);
                    } else {
                        Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                    }
                // If the user selects to pick an audio track from another application of the device
                } else if (items[item].equals(getString(R.string.action_audio_dialog_library))) {
                    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                    intent.setType("audio/*");
                    if (intent.resolveActivity(getPackageManager()) != null) {
                        startActivityForResult(Intent.createChooser(intent, getString(R.string.action_audio_dialog_title)), SELECT_AUDIO);
                    } else {
                        Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

        builder.show();
    }

    /**********************************************************************************************
     *               METHODS FOR INITIALIZATION AND SETTING OF THE LAYOUT COMPONENTS              *
     **********************************************************************************************/

    /**
     * Enable or disable controls to modify the current note based on the current EditState: ONLY VIEW or EDIT
     */
    public void onActivityStateChange() {
        /* Set the correct action bar icon to visible based on activity state: EDIT/ONLY VIEW */
        actionBarMenu.findItem(R.id.action_edit).setVisible(!editState);
        actionBarMenu.findItem(R.id.action_save).setVisible(editState);

        /* Set EditTexts of the note contents to Editable based on activity state: EDIT/ONLY VIEW */
        titleText.setFocusableInTouchMode(editState);
        titleText.setFocusable(editState);
        contentText.setFocusableInTouchMode(editState);
        contentText.setFocusable(editState);

        /* Recreate attachments DragSortListView  to show edit commands based on activity state: EDIT/ONLY VIEW */
        multimediaAdapter = new MultimediaItemAdapter(this, tabButtonAttachments, editState, attachments);
        multimediaDragSortListView.setAdapter(multimediaAdapter);
        multimediaDragSortListView.setDragEnabled(editState);
        multimediaDragSortListView.setRemoveEnabled(editState);
        bottomButtons.setVisibility((editState && mainAttachmentView.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);

        /* Recreate tags DragSortListView  to show edit commands based on activity state: EDIT/ONLY VIEW */
        tagAdapter = new TagItemAdapter(this, tabButtonTags, editState, tags);
        tagsDragSortListView.setAdapter(tagAdapter);
        tagsDragSortListView.setDragEnabled(editState);
        tagsDragSortListView.setRemoveEnabled(editState);
        tagLayout.setVisibility((editState && mainTagView.getVisibility() == View.VISIBLE) ? View.VISIBLE : View.GONE);

        /* Hide the software keyboard to prevent changes when the activity pass to the EDIT state */
        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(titleText.getWindowToken(), 0);
        inputMethodManager.hideSoftInputFromWindow(contentText.getWindowToken(), 0);
    }

    /**
     * Initialize the variables that store the current state for the activity, for example, the ID of the current note
     * @param savedInstanceState The state of the variables if the activity is destroyed by the system
     */
    public void initializeStateVariables(Bundle savedInstanceState) {

        // Verify if there is an instance state for a note and take its ID if exists
        if (savedInstanceState == null) {
            currentNoteId = null;
        } else {
            currentNoteId = (Long) savedInstanceState.getSerializable(KEY_NOTE_ID);
            editState = (Boolean) savedInstanceState.getSerializable(KEY_EDIT_STATE);
        }

        // Verify if it is received an intent to open a note from the Main Activity, take its ID from extras
        if (currentNoteId == null) {
            Bundle extras = getIntent().getExtras();
            currentNoteId = extras != null ? extras.getLong(MainScreenActivity.NOTE_ID) : null;
        }

        // Verify if there is an instance state for a note and take its EditState if exists
        if (savedInstanceState == null) {
            editState = (currentNoteId == null) ? true : false;
        } else {
            editState = (Boolean) savedInstanceState.getSerializable(KEY_EDIT_STATE);
        }

        // Verify if there is an instance state for a note and take its current tab if exists
        if (savedInstanceState == null) {
            currentTab = TAB_IDS[0];
        } else {
            currentTab = (Integer) savedInstanceState.getSerializable(KEY_CURRENT_TAB);
        }

        /* Retrieve a note from the database by its ID if set or create an empty note */
        if (savedInstanceState == null) {
            currentNote = (currentNoteId == null) ? new Note() : NotesManager.getNoteById(databaseAdapter, currentNoteId);
            oldNote = (currentNoteId == null) ? new Note() : NotesManager.getNoteById(databaseAdapter, currentNoteId);
        } else {
            currentNote = (Note) savedInstanceState.getParcelable(KEY_CURRENT_NOTE);
            oldNote = (Note) savedInstanceState.getParcelable(KEY_OLD_NOTE);
        }
    }

    /**
     * Bind view object with the views of the layout and initialize their listeners and base properties
     */
    private void initializeViews() {

        /* Show the content associated to the current tab */
        tabButtonText = (Button) findViewById(R.id.action_view_text);
        tabButtonAttachments = (Button) findViewById(R.id.action_view_attachments);
        tabButtonTags = (Button) findViewById(R.id.action_view_tags);
        mainScrollView = (ScrollView) findViewById(R.id.mainScrollView);
        mainAttachmentView = (LinearLayout) findViewById(R.id.mainAttachmentView);
        mainTagView = (LinearLayout) findViewById(R.id.mainTagView);
        tagLayout = (LinearLayout) findViewById(R.id.tagLayout);
        bottomButtons = (LinearLayout) findViewById(R.id.bottomButtons);

        Button tabButton = new Button(this);
        tabButton.setId(currentTab);
        onTabButtonSelected(tabButton);

        /* Initialize EditText used to write the note title and take its contents after change */
        titleText = (EditText) findViewById(R.id.titleText);

        titleText.addTextChangedListener(new TextWatcher() {
            public void afterTextChanged(Editable s) {
                currentNote.setTitle(titleText.getText().toString());
            }

            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
        });

        /* Initialize EditText used to write the note text content and take its contents after change */
        contentText = (EditText) findViewById(R.id.contentText);

        contentText.addTextChangedListener(new TextWatcher() {
            public void afterTextChanged(Editable s) {
                currentNote.setContent(contentText.getText().toString());
            }

            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
        });

        contentText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    contentText.setText(Html.fromHtml(contentText.getText().toString()));
                    contentText.setMovementMethod(LinkMovementMethod.getInstance());
                    currentNote.setContent(contentText.getText().toString());
                }
            }
        });

        /* Initialize TextView used to write the date of the last change of the note content */
        lastModifiedDate = (TextView) findViewById(R.id.lastModifiedDate);

        /* Initialize DragSortListView  for the note attachments and set the onClick listener to view them */
        emptyAttachmentView = (TextView) findViewById(R.id.emptyAttachmentView);

        multimediaDragSortListView  = (DragSortListView) findViewById(R.id.attachmentsDragSortListView);
        multimediaDragSortListView.setEmptyView(emptyAttachmentView);

        multimediaDragSortListView.setDropListener(new DragSortListView.DropListener() {
            @Override
            public void drop(int from, int to) {
                AttachmentItem item = (AttachmentItem) multimediaAdapter.getItem(from);
                multimediaAdapter.remove(from);
                multimediaAdapter.insert(item, to);
                multimediaAdapter.notifyDataSetChanged();
            }
        });

        multimediaDragSortListView.setRemoveListener(new DragSortListView.RemoveListener() {
            @Override
            public void remove(int which) {
                multimediaAdapter.remove(which);
                tabButtonAttachments.setText(tabButtonAttachments.getTag() + " (" + attachments.size() + ")");
            }
        });

        multimediaDragSortListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                AttachmentItem selectedAttachment = attachments.get(position);
                Uri attachmentUri = Uri.fromFile(MultimediaManager.encodeAttachmentFile(selectedAttachment));
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_VIEW);
                intent.setDataAndType(attachmentUri, selectedAttachment.getType());
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivityForResult(intent, REQUEST_VIEW);
                } else {
                    Toast.makeText(getBaseContext(), R.string.toast_no_intent, Toast.LENGTH_SHORT).show();
                }
            }
        });

        /* Initialize DragSortListView for the note tags */
        emptyTagView = (TextView) findViewById(R.id.emptyTagView);
        tagsDragSortListView  = (DragSortListView) findViewById(R.id.tagsDragSortListView);
        tagsDragSortListView.setEmptyView(emptyTagView);
        insertTag = (EditText) findViewById(R.id.insertTag);

        insertTag.setOnKeyListener(new View.OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                    onInsertTagSelected(new Button(getApplicationContext()));
                    return true;
                }
                return false;
            }
        });

        tagsDragSortListView.setDropListener(new DragSortListView.DropListener() {
            @Override
            public void drop(int from, int to) {
                String item = (String) tagAdapter.getItem(from);
                tagAdapter.remove(from);
                tagAdapter.insert(item, to);
                tagAdapter.notifyDataSetChanged();
            }
        });

        tagsDragSortListView.setRemoveListener(new DragSortListView.RemoveListener() {
            @Override
            public void remove(int which) {
                tagAdapter.remove(which);
                tabButtonTags.setText(tabButtonTags.getTag() + " (" + tags.size() + ")");
            }
        });
    }

    /**
     * Set the content of the view objects with the values associated to the current note
     */
    private void populateFields() {
        /* Set the action bar title based on the note title */
        String title = (currentNoteId == null) ? getString(R.string.action_add_note) : currentNote.getTitle();
        setTitle(title);

        /* Set the EditTexts content to the note title and the note text content */
        titleText.setText(currentNote.getTitle());
        contentText.setText(Html.fromHtml(currentNote.getContent()));
        contentText.setMovementMethod(LinkMovementMethod.getInstance());

        /* Set the date of the last change of the note */
        String lastChange = (currentNoteId == null) ? "" : getString(R.string.last_date_modified_message) + " " + currentNote.getLastChangeFormatter();
        lastModifiedDate.setText(lastChange);

        /* Set the DragSortListView  of the note attachments based on the attachment of the retrieved note */
        attachments = currentNote.getAttachments();
        multimediaAdapter = new MultimediaItemAdapter(this, tabButtonAttachments, editState, attachments);
        multimediaDragSortListView.setAdapter(multimediaAdapter);
        tabButtonAttachments.setText(tabButtonAttachments.getTag() + " (" + attachments.size() + ")");

        /* Set the DragSortListView  of the note tags based on the tags of the retrieved note */
        tags = currentNote.getTags();
        tagAdapter = new TagItemAdapter(this, tabButtonTags, editState, tags);
        tagsDragSortListView.setAdapter(tagAdapter);
        tabButtonTags.setText(tabButtonTags.getTag() + " (" + tags.size() + ")");
    }

    /**********************************************************************************************
     *                              RESULTS FROM OTHER ACTIVITIES                                 *
     **********************************************************************************************/

    /**
     * Receive the result of an intent send by this activity to an external application
     * @param requestCode The code of the request send by this activity
     * @param resultCode The code of the result send by the external application
     * @param data Extra data send by the external application as result
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            /* Declare the variable to store the received attachment */
            AttachmentItem resultAttachment = null;
            String positiveMessage = null;
            String negativeExtensionMessage = null;
            String negativeMaxSizeMessage = null;

            switch (requestCode) {
                case REQUEST_CAMERA: /* Receive an image obtained by the device camera */
                    resultAttachment = MultimediaManager.getAttachmentFromFile(NotesActivity.this, TEMP_IMAGE_FILE, AttachmentItem.TYPE_IMAGE);
                    positiveMessage = getString(R.string.toast_insert_image_attachment);
                    negativeExtensionMessage = getString(R.string.toast_image_not_supported_ext);
                    negativeMaxSizeMessage = getString(R.string.toast_image_not_supported_size) + " (" + MultimediaManager.ALLOWED_MAX_IMAGE_MB + ")";
                    break;
                case SELECT_IMAGE: /* Receive an image picked from an external application */
                    resultAttachment = MultimediaManager.getAttachmentFromUri(NotesActivity.this, data.getData(), AttachmentItem.TYPE_IMAGE);
                    positiveMessage = getString(R.string.toast_insert_image_attachment);
                    negativeExtensionMessage = getString(R.string.toast_image_not_supported_ext);
                    negativeMaxSizeMessage = getString(R.string.toast_image_not_supported_size) + " (" + MultimediaManager.ALLOWED_MAX_IMAGE_MB + ")";
                    break;
                case REQUEST_VIDEO: /* Receive a video obtained by the device camera */
                    resultAttachment = MultimediaManager.getAttachmentFromFile(NotesActivity.this, TEMP_VIDEO_FILE, AttachmentItem.TYPE_VIDEO);
                    negativeExtensionMessage = getString(R.string.toast_video_not_supported_ext);
                    negativeMaxSizeMessage = getString(R.string.toast_video_not_supported_size) + " (" + MultimediaManager.ALLOWED_MAX_VIDEO_MB + ")";
                    break;
                case SELECT_VIDEO: /* Receive a video picked from an external application */
                    resultAttachment = MultimediaManager.getAttachmentFromUri(NotesActivity.this, data.getData(), AttachmentItem.TYPE_VIDEO);
                    positiveMessage = getString(R.string.toast_insert_video_attachment);
                    negativeExtensionMessage = getString(R.string.toast_video_not_supported_ext);
                    negativeMaxSizeMessage = getString(R.string.toast_video_not_supported_size) + " (" + MultimediaManager.ALLOWED_MAX_VIDEO_MB + ")";
                    break;
                case REQUEST_AUDIO: /* Receive an audio track obtained by the device recorder */
                case SELECT_AUDIO: /* Receive an audio track picked from an external application */
                    resultAttachment = MultimediaManager.getAttachmentFromUri(NotesActivity.this, data.getData(), AttachmentItem.TYPE_AUDIO);
                    positiveMessage = getString(R.string.toast_insert_audio_attachment);
                    negativeExtensionMessage = getString(R.string.toast_audio_not_supported_ext);
                    negativeMaxSizeMessage = getString(R.string.toast_audio_not_supported_size) + " (" + MultimediaManager.ALLOWED_MAX_AUDIO_MB + ")";
                    break;
                default:
            }

            // Verify if the attachment is received without errors
            if (resultAttachment != null && !resultAttachment.isEmpty()) {
                attachments.add(0, resultAttachment);
                multimediaAdapter.notifyDataSetChanged();
                tabButtonAttachments.setText(tabButtonAttachments.getTag() + " (" + attachments.size() + ")");
                Toast.makeText(getBaseContext(), positiveMessage, Toast.LENGTH_SHORT).show();
            } else if (resultAttachment == null) { // If the received attachment is with not supported extension
                Toast.makeText(getBaseContext(), negativeExtensionMessage, Toast.LENGTH_SHORT).show();
            } else if (resultAttachment.isEmpty()) { // If the received attachment is with not supported extension
                Toast.makeText(getBaseContext(), negativeMaxSizeMessage, Toast.LENGTH_SHORT).show();
            }
        }
    }

    /**********************************************************************************************
     *                              ASYNC TASK FOR FAT OPERATIONS                                 *
     **********************************************************************************************/

    /**
     * This private class save the note content into the database in a separate thread
     * @author Mirko Marras
     * @version 1.0
     */
    private class SaveTask extends AsyncTask<Void, Void, Void> {

        private ProgressDialog dialog;
        private NotesActivity activity;
        private boolean isBackButton;

        public SaveTask(NotesActivity activity, Boolean isBackButton) {
            this.activity = activity;
            this.isBackButton = isBackButton;
            this.dialog = new ProgressDialog(this.activity);
        }

        protected void onPreExecute() {
            this.dialog.setMessage(getString(R.string.progress_save_message));
            this.dialog.show();
        }

        @Override
        protected void onPostExecute(final Void success) {
            if (dialog.isShowing()) {
                dialog.dismiss();
            }

            /* If the note contains data to save, saves it and returns to the ONLY VIEW state */
            if (!currentNote.isEmpty()) {
                /* Show the new title for the note */
                setTitle(titleText.getText().toString());

                /* Show the new date of the last change for the note */
                lastModifiedDate.setText(getString(R.string.last_date_modified_message) + " " + NotesManager.getNoteById(databaseAdapter, currentNoteId).getLastChangeFormatter());

                Toast.makeText(getBaseContext(), R.string.toast_save_note, Toast.LENGTH_SHORT).show();
            }

            /* Check if after the save the app return to the notes list, otherwise change EditState */
            if(this.isBackButton) {
                NavUtils.navigateUpFromSameTask(this.activity);
            }
            else {
                editState = false;
                onActivityStateChange();
            }

        }

        protected Void doInBackground(final Void... args) {
            /* Verify if the note contains some data and create it or save it if exists */
            if (!currentNote.isEmpty()) {

            /* Verify if the note is already saved into the database, if not create it */
                if (currentNoteId == null) {
                    currentNoteId = NotesManager.createNote(databaseAdapter, currentNote);
                    currentNote.setId(currentNoteId);
                    oldNote = currentNote;
                } else { /* If the note already exists into the database */
                    NotesManager.updateNote(databaseAdapter, currentNote);
                }
            }
            return null;
        }

    }

    /**
     * This private class import the note content received inside a mail into the database in a separate thread
     * @author Mirko Marras
     * @version 1.0
     */
    private class ImportTask extends AsyncTask<Void, Void, Void> {

        private ProgressDialog dialog;
        private NotesActivity activity;

        public ImportTask(NotesActivity activity) {
            this.activity = activity;
            this.dialog = new ProgressDialog(this.activity);
        }

        protected void onPreExecute() {
            this.dialog.setMessage(getString(R.string.progress_import_message));
            this.dialog.show();
        }

        @Override
        protected void onPostExecute(final Void success) {
            /* Update the content of the views to show the content of the imported note*/
            populateFields();

            if (dialog.isShowing()) {
                dialog.dismiss();
            }

            Toast.makeText(getBaseContext(), R.string.toast_import_ok, Toast.LENGTH_SHORT).show();
        }

        protected Void doInBackground(final Void... args) {
            // Verify if it is received an intent to open a file that store a note, take its ID after import
            if (this.activity.getIntent().getData() != null) {
                String filePath = this.activity.getIntent().getData().getEncodedPath();
                currentNoteId = NotesManager.createNote(databaseAdapter, MultimediaManager.readFileFromMail(filePath));
                currentNote = NotesManager.getNoteById(databaseAdapter, currentNoteId);
                oldNote = currentNote;
            }
            return null;
        }

    }
}